package controle;

import interfaces.Interface;
import interfaces.InterfaceInicial;
import interfaces.InterfaceJogo;

import java.awt.Color;
import java.util.ArrayList;

import componentes.Alvo;
import componentes.Aviao;
import componentes.Ponto;
import componentes.Projetil;

/**
 * 
 * Classe Controle.java que representa o controle dos eventos no jogo
 * 
 * @desc Projeto BombarJ
 * @author Simone Carneiro Streitenberger 11888 CCO2003
 * @author Thiago Nascimento Comicio 11889 CCO2003
 * @professor Edison de Jesus
 * @disciplina Computação Gráfica 2 - CCO812
 * 
 */
public class Controle {

	// atributos fases
	private final Color coresFases[] = { new Color(253, 189, 147),
			new Color(209, 255, 176), new Color(158, 239, 245),
			new Color(224, 182, 250), new Color(221, 203, 159) };

	// atributos aviao
	private double deslocAviaoFases[] = { 0.05, 0.055, 0.060, 0.065, 0.07 };

	private double tamOndas[] = { 30.0, 50.0, 80.0 };

	// atributo alvos
	private int alvoDistanciaEntre = 80;

	private int alvoIncio = 130;

	private int alvosLarguraFase[] = { 30, 25, 15, 10, 5 };

	private int alvoAlturaFase[] = { 150, 125, 100, 75, 50 };

	private int alvoVelocidadeFase[] = { 1, 2, 3 };

	private ArrayList<Alvo> alvos;

	private FaseThread faseThread;

	// interfaces
	private InterfaceInicial interfaceInicial;

	private InterfaceJogo interfaceJogo;

	private Interface inter;

	// indicação de pausa
	private boolean pause = false;

	// variáveis de fase
	private Aviao aviao;

	private Projetil projetil;

	private ArrayList<ProjetilThread> projeteisThreads;

	private int alvosAcertar = 0;

	private int alvosAcertados = 0;

	private int faseAtual;

	private int pontuacao;

	private int numProjeteis;

	private int numProjeteisExtra;

	/**
	 * 
	 * Construtor da classe Controle.java
	 * 
	 */
	public Controle() {
		// inicializa a classe de controle
		inicializar();
	}

	/**
	 * 
	 * Método que tem por função inicializar a classe de controle
	 * 
	 */
	private void inicializar() {

		// emite um beep indicando o início do programa
		SomThread som = new SomThread("sons/beep_1.au");
		som.start();

		// instancia a interface e a conecta a este controle
		inter = new Interface();
		inter.concetaControle(this);

		// instancia a lista de alvos e projéteis
		alvos = new ArrayList<Alvo>();
		projeteisThreads = new ArrayList<ProjetilThread>();

		// indica q não existe fase atual
		faseAtual = 0;

		// apresenta a tele inicial
		apresentaTelaInicial();
	}

	public void apresentaTelaInicial() {

		// indica que a fase atual não existe
		faseAtual = 0;

		// se não existir a interface inicial crie-a
		if (interfaceInicial == null) {
			interfaceInicial = new InterfaceInicial();
			interfaceInicial.conectaControle(this);
		}

		// se houverem alvos, estes serão exluídos
		if (alvos != null)
			alvos.clear();

		// destrói a thread responsável pelos elementos da fase
		if (faseThread != null)
			faseThread.destrua();

		// remove os alvos da interface de jogo
		if (interfaceJogo != null)
			interfaceJogo.getPainelFase().removeAlvos();

		// remove qualquer elemento do painel de conteudo da interface
		inter.getPainelConteudo().removeAll();

		// adiciona a interface inicial ao painel de conteudo
		inter.getPainelConteudo().add(interfaceInicial.getPainelInicial());
		inter.getPainelConteudo().setVisible(false);
		inter.getPainelConteudo().setVisible(true);
	}

	/**
	 * 
	 * Método que tem por função apresentar a interface que contém a primeira
	 * fase
	 * 
	 */
	public void apresentaTelaFasesInicial() {

		// inicializa as variáveis para a primeira fase
		faseAtual = 1;
		pontuacao = 0;
		numProjeteisExtra = 5;

		// verifica se existe uma interface de jogo para não ser necessário
		// criá-la novamente
		if (interfaceJogo == null) {
			interfaceJogo = new InterfaceJogo();
			interfaceJogo.conectaControle(this);
		}

		// remove os elementos do painel
		inter.getPainelConteudo().removeAll();
		inter.getPainelConteudo().setVisible(false);

		// adiciona a interface de jogo no painel de conteudo
		inter.getPainelConteudo().add(interfaceJogo);

		// indica o nome do jogador
		interfaceJogo.setNomeJogador(interfaceInicial.getNomeJogador());

		// seta os pontos iniciais
		interfaceJogo.setPontos(pontuacao);

		// setando os projéteis extra para cor azul
		interfaceJogo.setProjeteisExtra();

		// indica que o jogo não está terminado
		interfaceJogo.getPainelFase().setTerminaJogo(false);

		// apresenta a fase atual ao jogador
		apresentaFase(faseAtual);

		// seta o painel de conteúdo como visível
		inter.getPainelConteudo().setVisible(true);
	}

	/**
	 * 
	 * 
	 * Método que tem por função apresentar uma fase de acordo com o parâmetro
	 * passado
	 * 
	 * @param fase
	 *            Indicador da fase que deverá ser exibida.
	 * 
	 */
	public void apresentaFase(int fase) {

		// iniciando a fase com sua cor
		interfaceJogo.setBackground(coresFases[fase - 1]);
		interfaceJogo.setFase(fase);

		// iniciando o o numero de projeteis por fase
		numProjeteis = 7;
		interfaceJogo.setProjeteisFase();

		// destruindo fase anterior
		if (faseThread != null) {
			faseThread.destrua();
		}

		// removendo alvos e projeteis de impressão
		interfaceJogo.getPainelFase().removeAlvos();
		interfaceJogo.getPainelFase().removeProjeteis();

		// destruindo as Threads dos projeteis
		for (ProjetilThread projetilThread : projeteisThreads) {
			projetilThread.destrua();
		}
		projeteisThreads.clear();

		// alvos a acertar
		alvosAcertar = 5;
		alvosAcertados = 0;

		// criando aviao
		aviao = new Aviao(new Ponto(-80, 75));
		interfaceJogo.getPainelFase().setAviao(aviao);

		// criando os alvos
		Alvo alvo;
		alvos.clear();

		// cria os novos alvos e os adiciona no painel de jogo
		for (int i = 0; i < 5; i++) {
			alvo = new Alvo(alvoAlturaFase[fase - 1],
					alvosLarguraFase[fase - 1], interfaceJogo.getPainelFase()
							.getHeight(), (i + 1) * alvoDistanciaEntre
							+ alvoIncio, Color.BLACK);

			alvos.add(alvo);
			interfaceJogo.getPainelFase().addAlvo(alvo);
		}

		// instancia a thread que cuidará dos elementos de uma determinada fase
		faseThread = new FaseThread(aviao, interfaceJogo.getPainelFase(),
				deslocAviaoFases[fase - 1], alvos, tamOndas[interfaceInicial
						.getNivel()], alvoVelocidadeFase[interfaceInicial
						.getNivel()]);

		// inicia a fase pausada
		faseThread.setPause(pause);
		// inicia a thread
		faseThread.start();

		// seta a interface de jogo como visível
		interfaceJogo.setVisible(true);

		// pausa os outros elemntos
		if (pause == false) {
			pause();
		}

		// seta a cor da fase e a fase
		interfaceJogo.getPainelFase().setCorFase(coresFases[fase - 1]);
		interfaceJogo.getPainelFase().setFase(fase);
	}

	public void soltaProjetil() {
		// verifica se existe uma thread controlando o avião
		if ((faseThread != null) && (!pause)
				&& ((numProjeteis > 0) || (numProjeteisExtra > 0))) {

			// exibe que o projétil foi atirado e seta os novos valores de
			// quantidade de projéteis
			if (numProjeteis > 0) {
				interfaceJogo.setProjetilUsado(numProjeteis);
				numProjeteis--;
			} else if (numProjeteisExtra > 0) {
				interfaceJogo.setProjetilExtraUsado(numProjeteisExtra);
				numProjeteisExtra--;
			}

			// cria um projétil na posicao do aviao
			projetil = new Projetil(aviao.getPosicaoCentroRelOrigem());

			// seta o projetil na fase
			interfaceJogo.getPainelFase().addProjetil(projetil);

			// inicia a thread de movimentação do projétil
			ProjetilThread projetilThread = new ProjetilThread(interfaceJogo
					.getPainelFase(), projetil, aviao, alvos,
					getCorFase(faseAtual), this);

			// adiciona na lista de threads de projétil
			projeteisThreads.add(projetilThread);

			// inicia a thread do projétil
			projetilThread.start();
		}
	}

	/**
	 * 
	 * Método que tem por função pausar o jogo
	 * 
	 */
	public void pause() {

		// verifica se existe a thread de controle da fase
		if (faseThread != null) {

			// verifica o número de projéteis
			if ((numProjeteis > 0) || (numProjeteisExtra > 0)) {

				// verifica se o jogo está pausado e despausa ou pausa o jogo
				if (pause) {

					// despausando o jogo

					faseThread.setPause(false);

					for (ProjetilThread projetilThread : projeteisThreads)
						if (projetilThread != null)
							projetilThread.setPause(false);

					interfaceJogo.getPainelFase().setPause(false);

					this.pause = false;
				} else {

					// pausando o jogo

					faseThread.setPause(true);

					for (ProjetilThread projetilThread : projeteisThreads)
						if (projetilThread != null)
							projetilThread.setPause(true);

					interfaceJogo.getPainelFase().setPause(true);

					this.pause = true;
				}

				// repinta o painel de interface de jogo
				interfaceJogo.getPainelFase().repaint();

			}
		}
	}

	/**
	 * 
	 * 
	 * Método que tem por função focar a interface, para que os eventos do
	 * teclado sejam captados
	 * 
	 * 
	 */
	public void focaInterface() {
		inter.requestFocus();
	}

	/**
	 * 
	 * Método que tem por função retornar a quantidade de projéteis que o
	 * usuário tem direito
	 * 
	 * @return Número de projéteis que o usuário tem direito.
	 * 
	 */
	public int getNumProjeteis() {
		return numProjeteis;
	}

	/**
	 * 
	 * Método que tem por função retornar a quantidade de projéteis extras que o
	 * usuário tem direito
	 * 
	 * @return Número de projéteis extras que o usuário tem direito.
	 * 
	 */
	public int getNumProjeteisExtra() {
		return numProjeteisExtra;
	}

	/**
	 * 
	 * Método que tem por função retornar a quantidade de alvos que ainda devem
	 * ser acertados
	 * 
	 * @return Quantidade de alvos que devem ser acertados em uma fase.
	 * 
	 */
	public int getAlvosAcertar() {
		return (alvosAcertar - alvosAcertados);
	}

	/**
	 * 
	 * Método que tem por função indicar o fim de um jogo onde o jogador perdeu
	 * 
	 */
	public void terminaJogo() {

		// verifica se existe uma thread de controle de fase
		if (faseThread != null) {
			// pausa a thread de fases
			faseThread.setPause(true);

			// remove os projéteis para que não sejam impressos
			interfaceJogo.getPainelFase().removeProjeteis();

			// indica o término do jogo pra que o painel imprima a mensagem de
			// término
			interfaceJogo.getPainelFase().setTerminaJogo(true);

			// destrói as threads de projétil
			for (ProjetilThread projetilThread : projeteisThreads) {
				projetilThread.destrua();
			}
		}

		// repinta o painel de interface do jogo
		interfaceJogo.getPainelFase().repaint();

	}

	/**
	 * 
	 * Método que tem por função indicar o fim de um jogo onde o jogador ganhou
	 * 
	 */
	public void ganhaJogo() {

		// verifica se existe uma thread de controle de fase
		if (faseThread != null) {
			// pausa a thread de fase
			faseThread.setPause(true);

			// remove os projéteis lançados
			interfaceJogo.getPainelFase().removeProjeteis();

			// exibe a mensagem de jogo ganho
			interfaceJogo.getPainelFase().setGanhaJogo(true);

			// destrói as threads de projéteis
			for (ProjetilThread projetilThread : projeteisThreads) {
				projetilThread.destrua();
			}

			// zera os valores de quantidade de projéteis
			numProjeteis = 0;
			numProjeteisExtra = 0;

		}

		// repinta a interface de jogo
		interfaceJogo.getPainelFase().repaint();

	}

	/**
	 * 
	 * Método que tem por função retornar a cor da fase atual
	 * 
	 * @param fase
	 *            Fase atual
	 * @return Cor da fase atual
	 * 
	 */
	public Color getCorFase(int fase) {
		return coresFases[fase - 1];
	}

	/**
	 * 
	 * Método que tem por função indicar o acerto de um alvo
	 * 
	 */
	public void acertoAlvo() {
		// aumenta a quantidade de aalvos acertados
		alvosAcertados++;

		// cálculo da pontuação e exibição
		int nivel = interfaceInicial.getNivel() + 1;
		pontuacao += (nivel) * (2 * nivel + 3) + faseAtual * 1;
		interfaceJogo.setPontos(pontuacao);

		// verifica se todos os alvos foram acertados, se sim passa de fase ou
		// termina o jogo com vitória
		if (alvosAcertados == alvosAcertar) {
			if (faseAtual == 5) {
				ganhaJogo();
			} else {
				apresentaFase(++faseAtual);
			}
		}
	}

	/**
	 * 
	 * Método que tem por função verificar o reacerto de um alvo
	 * 
	 */
	public void reacertoAlvo() {
		alvosAcertados--;

		// cálculo da pontuação e exibição na interface de jogo
		int nivel = interfaceInicial.getNivel() + 1;
		pontuacao -= (nivel) * (2 * nivel + 3) + faseAtual * 1;
		interfaceJogo.setPontos(pontuacao);
	}

	/**
	 * 
	 * Método que retorna o valor do atributo pause.
	 * 
	 * @return O atributo pause.
	 * 
	 */
	public boolean isPause() {
		return pause;
	}
}
